package org.skydiveapplibrary.repository;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.skydiveapplibrary.data.Database;
import org.skydiveapplibrary.data.Location;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class LocationRepository
{
	// jump count query format
	private static final String JUMP_COUNT_QUERY_FORMAT =
		"select count(*) " +
		"from log_entry " +
		"where location = {0}";
	
	private Database database;
	
	public LocationRepository(Context context)
	{
		database = new Database(context);
	}
	
	public List<Location> getAllLocations()
	{
		return getLocations(null);
	}
	
	public List<Location> getLocations()
	{
		return getLocations(Database.LOCATION_ACTIVE + " = " + 1);
	}
	
	private List<Location> getLocations(String where)
	{
		List<Location> locations = new ArrayList<Location>();
		
		SQLiteDatabase db = database.getReadableDatabase();
		Cursor cursor = null;
		try
		{
			// perform query
			cursor = db.query(
					Database.LOCATION_TABLE,
					Database.LOCATION_ALL_COLUMNS,
					where,
					null, null, null,
					Database.LOCATION_NAME);
			
			// iterate over results
			while (cursor.moveToNext())
			{
				// create locations
				Location location = createLocation(cursor);
				locations.add(location);
			}
		}
		finally
		{
			// close cursor
			if (cursor != null)
			{
				cursor.close();
			}
			// close db
			db.close();
		}
		
		return locations;
	}
	
	public Location findLocation(String name)
	{
		return getLocation(Database.LOCATION_NAME + " = ?", name);
	}
	
	public Location getLocation(int id)
	{
		return getLocation(Database.LOCATION_ID + " = ?", String.valueOf(id));
	}
	
	public Location getFromUniqueId(String uniqueId)
	{
		return getLocation(Database.LOCATION_UNIQUE_ID + " = ?", uniqueId);
	}
	
	private Location getLocation(String where, String... whereParams)
	{
		Location location = null;
		
		SQLiteDatabase db = database.getReadableDatabase();
		Cursor cursor = null;
		try
		{
			// perform query
			cursor = db.query(
					Database.LOCATION_TABLE,
					Database.LOCATION_ALL_COLUMNS,
					where,
					whereParams,
					null,
					null,
					null);
			
			if (cursor.moveToFirst())
			{
				location = createLocation(cursor);
			}
		}
		finally
		{
			// close cursor
			if (cursor != null)
			{
				cursor.close();
			}
			// close db
			db.close();
		}
		
		return location;
	}
	
	public Location getHomeLocation()
	{
		Location location = null;
		
		SQLiteDatabase db = database.getReadableDatabase();
		Cursor cursor = null;
		try
		{
			// perform query
			cursor = db.query(
					Database.LOCATION_TABLE,
					Database.LOCATION_ALL_COLUMNS,
					Database.LOCATION_HOME + " = " + 1,
					null, null, null, null);
			
			if (cursor.moveToFirst())
			{
				location = createLocation(cursor);
			}
		}
		finally
		{
			// close cursor
			if (cursor != null)
			{
				cursor.close();
			}
			// close db
			db.close();
		}
		
		return location;
	}
	
	public void addLocation(Location location)
	{
		// set modified
		if (location.getLastModifiedUtc() == null)
		{
			location.setLastModifiedUtc(new Date(System.currentTimeMillis()));
		}
		
		//set uniqueId
		if (location.getUniqueId() == null || location.getUniqueId() == "")
		{
			location.setUniqueId(UUID.randomUUID().toString());
		}
		
		// get insert values
		ContentValues values = createContentValues(location);
		
		// do insert
		SQLiteDatabase db = database.getWritableDatabase();
		// start tx
		db.beginTransaction();
		try
		{
			if (location.isHome())
			{
				// clear other home locations
				clearHomeLocation(db);
			}
			
			// do insert
			long id = db.insert(Database.LOCATION_TABLE, null, values);
			// update id
			location.setId((int)id);
			
			// set tx success
			db.setTransactionSuccessful();
		}
		finally
		{
			// end tx
			db.endTransaction();
			// close db
			db.close();
		}
	}

	public void updateLocation(Location location)
	{
		updateLocation(location, true);
	}
	
	public void updateLocation(Location location, boolean updateLastModified)
	{
		// set modified
		if (updateLastModified)
		{
			location.setLastModifiedUtc(new Date(System.currentTimeMillis()));
		}
		
		// get update values
		ContentValues values = createContentValues(location);
		
		SQLiteDatabase db = database.getWritableDatabase();
		// start tx
		db.beginTransaction();
		try
		{
			if (location.isHome())
			{
				// clear other home locations
				clearHomeLocation(db);
			}
			
			// do update
			db.update(Database.LOCATION_TABLE, values,
					Database.LOCATION_ID + " = " + location.getId(),
					null);
			
			// set tx success
			db.setTransactionSuccessful();
		}
		finally
		{
			// end tx
			db.endTransaction();
			// close db
			db.close();
		}
	}
	
	public void deleteLocation(Location location)
	{
		SQLiteDatabase db = database.getWritableDatabase();
		try
		{
			// create update values
			ContentValues values = new ContentValues();
			values.put(Database.LOCATION_ACTIVE, false);
					
			// deactivate location
			db.update(
					Database.LOCATION_TABLE,
					values,
					Database.LOCATION_ID + " = " + location.getId(),
					null);
		}
		finally
		{
			// close db
			db.close();
		}
	}
	
	private void clearHomeLocation(SQLiteDatabase db)
	{
		ContentValues values = new ContentValues();
		values.put(Database.LOCATION_HOME, 0);
		
		db.update(
				Database.LOCATION_TABLE,
				values,
				null, null);
	}
	
	private Location createLocation(Cursor cursor)
	{
		Location location = new Location();
		
		location.setId(Database.getInteger(cursor, Database.LOCATION_ID));
		location.setActive(Database.getBoolean(cursor, Database.LOCATION_ACTIVE));
		location.setName(Database.getString(cursor, Database.LOCATION_NAME));
		location.setHome(Database.getBoolean(cursor, Database.LOCATION_HOME));
		location.setNotes(Database.getString(cursor, Database.LOCATION_NOTES));
		location.setJumpCount(getJumpCount(location.getId(), database.getReadableDatabase()));
		location.setLastModifiedUtc(Database.getDateTime(cursor, Database.LOCATION_LAST_MODIFIED_UTC));
		location.setUniqueId(Database.getString(cursor, Database.LOCATION_UNIQUE_ID));
		
		return location;
	}
	
	private ContentValues createContentValues(Location location)
	{
		ContentValues values = new ContentValues();
		
		values.put(Database.LOCATION_ACTIVE, location.isActive());
		values.put(Database.LOCATION_NAME, location.getName());
		values.put(Database.LOCATION_HOME, location.isHome());
		values.put(Database.LOCATION_NOTES, location.getNotes());
		values.put(Database.LOCATION_LAST_MODIFIED_UTC, Database.dateTimeToString(location.getLastModifiedUtc()));
		values.put(Database.LOCATION_UNIQUE_ID, location.getUniqueId());
		
		return values;
	}
	
	private int getJumpCount(int locationId, SQLiteDatabase db)
	{
		// construct query
		String query = MessageFormat.format(JUMP_COUNT_QUERY_FORMAT, locationId);
		// result
		int result = 0;
		
		// execute query
		Cursor cursor = null;
		try
		{
			cursor = db.rawQuery(query, null);
			
			// get results
			if (cursor.moveToFirst())
			{
				result = cursor.getInt(0);
			}
		}
		finally
		{
			// close cursor
			if (cursor != null)
			{
				cursor.close();
			}
		}
		
		return result;
	}
	
	public boolean exists(String where, String... whereParams)
	{
		SQLiteDatabase db = database.getReadableDatabase();
		Cursor cursor = null;
		try
		{
			// perform query
			cursor = db.query(
					Database.LOCATION_TABLE,
					Database.LOCATION_ALL_COLUMNS,
					where + " = ?",
					whereParams,
					null,
					null,
					null);
			
			return cursor.getCount() > 0;
		}
		finally
		{
			// close cursor
			if (cursor != null)
			{
				cursor.close();
			}
			// close db
			db.close();
		}
	}
}